package com.ics.cmsadmin.frame.aop;

import com.ics.cmsadmin.frame.core.annotation.Authorize;
import com.ics.cmsadmin.frame.core.bean.ApiResponse;
import com.ics.cmsadmin.frame.core.enums.AuthorizeEnum;
import com.ics.cmsadmin.frame.core.enums.AuthorizeTypeEnum;
import com.ics.cmsadmin.frame.core.exception.ExceptionUtils;
import com.ics.cmsadmin.frame.utils.GsonUtils;
import com.ics.cmsadmin.modules.auth.bean.SysAuthorize;
import com.ics.cmsadmin.modules.sso.utils.SsoUtils;
import com.ics.cmsadmin.modules.system.bean.UserOperLogBean;
import com.ics.cmsadmin.modules.system.service.UserOperLogService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * redis缓存切面
 * Created by Administrator on 2017/6/23.
 */
@Aspect
@Component
@Log4j2
public class ControllerAop {

    @Resource
    private HttpServletRequest request;

    @Resource
    private UserOperLogService userOperLogService;

    @Pointcut("execution(com.ics.cmsadmin.frame.core.bean.ApiResponse com.ics.cmsadmin.modules.*.controller.*Controller.*(..))")
    public void controllerMethod() {
    }


    @Around("controllerMethod()")
    public ApiResponse dbCacheMember(ProceedingJoinPoint pjd) {
        String controllerMethod = pjd.getSignature().toShortString();
        long startTime = System.currentTimeMillis();
        try {
            List<Object> collect = Arrays.stream(pjd.getArgs()).filter(item -> {
                boolean isRequest = item instanceof HttpServletRequest;
                boolean isResponse = item instanceof HttpServletResponse;
                boolean isBindingResult = item instanceof BindingResult;
                return !isRequest && !isResponse && !isBindingResult;
            }).collect(Collectors.toList());
            log.info("控制层切面方法 {} 准备开始, 请求参数是: {}", controllerMethod, GsonUtils.toJson(collect));
            Object proceed = pjd.proceed();
//            log.info("控制层切面方法 {} 返回值为: {}", controllerMethod, GsonUtils.toJson(proceed));
            // ((MethodInvocationProceedingJoinPoint) pjd).methodInvocation.getMethod().getAnnotation(Authorize.class).value()
            insertUserOperLog(pjd, collect, proceed);
            return (ApiResponse) proceed;
        } catch (Throwable throwable) {
            log.info("控制层切面方法 {} 抛出异常, 异常堆栈信息为:{}", controllerMethod, ExceptionUtils.collectExceptionStackMsg(throwable));
            return new ApiResponse(throwable);
        } finally {
            long endTime = System.currentTimeMillis();
            log.info("控制层切面方法 {} 执行完毕,总耗时 {} 毫秒.", controllerMethod, (endTime - startTime));
        }
    }

    private void insertUserOperLog(ProceedingJoinPoint pjd, List<Object> params, Object resultObject) {
        try {
            String loginUserId = SsoUtils.getLoginUserId(request);
            if (StringUtils.isBlank(loginUserId)){
                return;
            }
            Signature s = pjd.getSignature();
            MethodSignature ms = (MethodSignature)s;
            Method m = ms.getMethod();
            Authorize authorize = m.getAnnotation(Authorize.class);
            if (authorize == null) {
                return;
            }
            long operateCount = Stream.concat(Arrays.stream(authorize.value()), Arrays.stream(authorize.any()))
                .filter(item -> item.getType() != AuthorizeTypeEnum.QUERY)
                .count();
            if (operateCount == 0){
                return;
            }
            ApiOperation apiOperation = m.getAnnotation(ApiOperation.class);
            String desc = apiOperation == null ? "" : apiOperation.value();
            userOperLogService.insert(UserOperLogBean.builder()
                .userId(loginUserId)
                .operIp(request.getRemoteHost())
                .requestUri(request.getRequestURI())
                .requestMethod(pjd.getSignature().toShortString())
                .requestDesc(desc)
                .requestParams(GsonUtils.toJson(params))
                .returnResult(GsonUtils.toJson(resultObject))
                .build());
        }catch (Exception e){
            log.warn("插入用户操作日志异常 " + ExceptionUtils.collectExceptionStackMsg(e));
        }
    }

}
